#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>

using namespace std;
// 自定义一个信号处理的方式
void handler(int signal_id)
{
    cout << "调用该命令时产生了一个：" << signal_id << " 信号" << endl;
    sleep(2);
    exit(1);
}
// signal捕捉
void test1()
{
    // 向signal函数中 传入系统中定义的信号名、自定义的处理方式
    signal(2, handler);
    while (1)
    {
        cout << "process is running, pid: " << getpid() << endl;
        sleep(2);
    }
}
void test2()
{
    // 向signal函数中 传入系统中定义的信号名、自定义的处理方式
    signal(8, handler);
    // cout << 10 / 0 << endl;
}
void test3()
{

    // 信号处理的三种方式

    // 信号的忽略
    // signal(2， SIG_IGN);
    // 信号的默认
    // signal(3, SIG_DFL);
    // 信号的自定义捕捉
    // signal(4, handler);

    // #define SIG_ERR ((__sighandler_t)-1) /* Error return.  */
    // #define SIG_DFL ((__sighandler_t)0)  /* Default action.  */
    // #define SIG_IGN ((__sighandler_t)1)  /* Ignore signal.  */

    // 没有收到信号可以对信号进行递达
    // 没有收到信号可以对信号进行阻塞
}
// 进行信号屏蔽
void test4()
{
    signal(2, handler);
    sigset_t block, oblock;
    // 初始化信号集
    sigemptyset(&block);
    sigemptyset(&oblock);

    // 在栈上对临时变量block对2号对应的block表进行由0置1
    sigaddset(&block, 2);

    // 把这个block表应用到系统层面上，实现将2信号添加进信号屏蔽字中
    sigprocmask(SIG_BLOCK, &block, &oblock);
    int count = 10;
    while (1)
    {
        cout << "process is running, pid is: " << getpid() << endl;
        sleep(1);
        count--;
        if (count == 0)
            break;
    }
    cout << "取消对2号信号的屏蔽" << endl;
    // 当取消屏蔽后，如果操作系统发送了2号信号，2号信号此时会调用
    sigprocmask(SIG_UNBLOCK, &block, &oblock);
    while (1)
    {
        cout << "你好啊，pid: " << getpid() << endl;
        sleep(1);
    }
}

void PrintSet(const sigset_t &set)
{
    // 通过二进制形式打印
    for (int i = 32; i > 0; i--)
    {
        // 判断这个set中有没有这下标对应的信号
        if (sigismember(&set, i) == 1)
            cout << "1";
        else
            cout << "0";
    }
    cout << endl;
    sleep(2);
}
void test5()
{
    signal(2, handler);
    signal(3, handler);
    sigset_t block;
    sigset_t oblock;
    // 初始化信号集
    sigemptyset(&block);
    sigemptyset(&oblock);

    // 在栈上对临时变量block对2号对应的block表进行由0置1
    sigaddset(&block, 2);
    sigaddset(&block, 3);

    // 把这个block表应用到系统层面上，实现将2信号添加进信号屏蔽字中
    sigprocmask(SIG_BLOCK, &block, &oblock);

    cout << "process is running, pid is: " << getpid() << endl;
    sleep(2);
    int count = 5;
    while (1)
    {
        sigset_t pending;
        sigpending(&pending);
        // 通过二进制形式打印待处理集合
        PrintSet(pending);
        if (count-- == 0)
        {
            cout << "关闭对信号的屏蔽" << endl;
            // 将屏蔽字设置为全空
            sigprocmask(SIG_SETMASK, &oblock, nullptr);
        }
    }
}
void test6()
{
    struct sigaction act;
    struct sigaction oact;

    // sa_mask为存储阻塞的信号集

    sigemptyset(&act.sa_mask);
    sigaddset(&act.sa_mask, 1);
    sigaddset(&act.sa_mask, 3);

    act.sa_handler = handler;

    // 修改handler表
    // 填入信号编号，实现和signal一样的功能
    sigaction(2, &act, &oact);

    // Linux在接收到某一个信号时，如果该信号正在递达，并没有结束
    // 那么如果再次接收时该信号时，操作系统会将其屏蔽，直至结束
}
void handler_child(int signo)
{
    // 如果进程退出能够进入该函数则验证：子进程退出会返回信号
    cout << "get a signo: " << signo << endl;

    pid_t id = 0;
    // 循环等待
    while ((id = waitpid(-1, nullptr, WNOHANG)))
    {
        if (id <= 0)
            break;
        cout << "回收进程：" << id << endl;
    }
}
// 基于信号对子进程进行回收
void test7()
{
    signal(SIGCHLD, handler_child);
    // signal(SIGCHLD, SIG_IGN); // Linux支持手动忽略SIGCHLD, 所有的子进程都不要父进程进行等待了！！退出自动回收Z
    // 模拟多个子进程的创建

    for (int i = 0; i < 10; i++)
    {
        pid_t id = fork();
        if (id == 0)
        {
            cout << "child is running" << endl;
            sleep(5);
            exit(1);
        }
    }

    // 单个子进程的回收
    // pid_t id = fork();
    // if (id == 0)
    // {
    //     cout << "child is running" << endl;
    //     sleep(5);
    //     exit(10);
    // }

    while(1)
    {
        // 父进程代码模块
        sleep(1);
    }
}
int main()
{
    test6();
}
